package aceim.protocol.snuk182.mrim.inner;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import aceim.protocol.snuk182.mrim.MrimEntityAdapter;
import aceim.protocol.snuk182.mrim.inner.dataentity.MrimFileTransfer;
import aceim.protocol.snuk182.mrim.inner.dataentity.MrimIncomingFile;
import aceim.protocol.snuk182.mrim.inner.dataentity.MrimPacket;
import aceim.protocol.snuk182.mrim.utils.ProtocolUtils;
import android.os.Environment;
public class FileTransferEngine {
private static final String MRA_FT_HELLO = "MRA_FT_HELLO ";
private static final String MRA_GET_FILE = "MRA_FT_GET_FILE ";
private static final String LIST_DATA_DIVIDER = ";";
private static final String IN_DATA_DIVIDER = ":";
private static final int SERVER_SOCKET_TIMEOUT = 600000;
private static final String MrimProxyName = "mrim10-3.mail.ru";
private byte[] localIp = new byte[] { 0, 0, 0, 0 };
private final MrimServiceInternal service;
private final List<MrimFileTransfer> transfers = new ArrayList<MrimFileTransfer>();
private final Map<Long, FileRunnableService> activeTransfers = new HashMap<Long, FileRunnableService>();
private List<NotificationData> notifications = new LinkedList<NotificationData>();
public FileTransferEngine(MrimServiceInternal service) {
this.service = service;
}
public long sendFiles(String buddyMrid, final List<File> files, byte[] localIp, int messageId) {
final MrimFileTransfer transfer = getFileTransferRequest(buddyMrid, files, messageId);
this.localIp = localIp;
transfers.add(transfer);
new Thread("File sender " + buddyMrid) {
@Override
public void run() {
try {
sendFileTransferRequest(createPeer(transfer));
} catch (IOException e) {
// todo ask mirror?
}
}
}.start();
return transfer.messageId;
}
private FileRunnableService createPeer(MrimFileTransfer transfer) throws IOException {
service.log("ft: creating own peer");
FileRunnableService frs = activeTransfers.get(transfer.messageId);
if (frs == null) {
service.log("ft: new runnable for " + transfer.buddyMrid);
frs = new FileRunnableService(transfer);
frs.connectionState = FileRunnableService.CONNSTATE_HANDSHAKE;
activeTransfers.put((long) transfer.messageId, frs);
} else {
service.log("ft: existing runnable for " + transfer.buddyMrid);
frs.transfer = transfer;
}
frs.connectionState = FileRunnableService.CONNSTATE_HANDSHAKE;
frs.server = createLocalSocket(frs);
/*
* message.externalPort = frs.server.getLocalPort(); message.rvIp =
* ProtocolUtils.getIPString(service.getInternalIp());
* message.rvMessageType = 0;
*/
return frs;
}
private void sendFileTransferRequest(FileRunnableService frs) {
String myIpPortStr = ProtocolUtils.getIPString(localIp) + IN_DATA_DIVIDER + frs.server.getLocalPort() + LIST_DATA_DIVIDER;
service.log("--- my ipport " + myIpPortStr);
MrimPacket packet = new MrimPacket();
packet.type = MrimConstants.MRIM_CS_FILE_TRANSFER;
byte[] to = MrimEntityAdapter.string2lpsa(frs.transfer.buddyMrid);
byte[] sessionId = ProtocolUtils.int2ByteLE(frs.transfer.messageId);
long ln = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < frs.transfer.files.size(); i++) {
File file = frs.transfer.files.get(i);
ln += file.length();
sb.append(file.getName().replaceAll(IN_DATA_DIVIDER, "_").replaceAll(LIST_DATA_DIVIDER, "_"));
// sb.append(IN_DATA_DIVIDER);
sb.append(LIST_DATA_DIVIDER);
sb.append(file.length());
sb.append(LIST_DATA_DIVIDER);
}
byte[] lengthSum = ProtocolUtils.long2ByteLE(ln);
byte[] filenames = MrimEntityAdapter.string2lpsa(sb.toString());
byte[] unk1 = MrimEntityAdapter.string2lpsa("");
byte[] myIpPort = MrimEntityAdapter.string2lpsa(myIpPortStr);
byte[] internalData = new byte[to.length + sessionId.length + 4 + 4 + filenames.length + unk1.length + myIpPort.length];
int i = 0;
System.arraycopy(to, 0, internalData, i, to.length);
i += to.length;
System.arraycopy(sessionId, 0, internalData, i, sessionId.length);
i += sessionId.length;
System.arraycopy(lengthSum, 0, internalData, i, 4);
i += 4;
System.arraycopy(ProtocolUtils.int2ByteLE(filenames.length + unk1.length + myIpPort.length), 0, internalData, i, 4);
i += 4;
System.arraycopy(filenames, 0, internalData, i, filenames.length);
i += filenames.length;
System.arraycopy(unk1, 0, internalData, i, unk1.length);
i += unk1.length;
System.arraycopy(myIpPort, 0, internalData, i, myIpPort.length);
i += myIpPort.length;
packet.rawData = internalData;
service.getRunnableService().sendToSocket(packet);
}
private byte[] getHandshakeData(MrimFileTransfer transfer) {
service.log("get handshake for " + transfer.host + " id " + transfer.messageId);
String str = new String(MRA_FT_HELLO + service.getMrid());
byte[] boo = new byte[str.length() + 1];
System.arraycopy(str.getBytes(), 0, boo, 0, str.length());
boo[boo.length - 1] = 0;
return boo;
}
private ServerSocket createLocalSocket(final FileRunnableService frs) throws IOException {
final ServerSocket server = new ServerSocket(0);
new Thread() {
@Override
public void run() {
try {
setName("FT Server socket listener " + server.getLocalPort());
server.setSoTimeout(SERVER_SOCKET_TIMEOUT);
Socket socket = server.accept();
frs.socket = socket;
service.log("client connected");
frs.start();
} catch (Exception e) {
service.log(e);
}
}
}.start();
return server;
}
private MrimFileTransfer getFileTransferRequest(String buddyMrid, List<File> files, int messageId) {
MrimFileTransfer transfer = new MrimFileTransfer();
transfer.buddyMrid = buddyMrid;
transfer.files = files;
transfer.messageId = messageId;
return transfer;
}
class FileRunnableService extends Thread {
public static final int CONNSTATE_CONNECTED = 0;
public static final int CONNSTATE_HANDSHAKE = 1;
public static final int CONNSTATE_FILE_HEADER = 2;
public static final int CONNSTATE_FILE_BODY = 3;
public static final int CONNSTATE_FILE_SENT = 4;
public static final int CONNSTATE_DISCONNECTED = 5;
ServerSocket server = null;
Socket socket;
int connectionState = CONNSTATE_CONNECTED;
MrimFileTransfer transfer;
String participantUid = null;
List<byte[]> blobs = new LinkedList<byte[]>();
long currentFileSizeLeft = 0;
long currentFileSize = 0;
byte[] currentFileInfo = null;
private ExtendedBufferedOutputStream currentFileStream;
byte[] buffer = null;
public FileRunnableService(MrimFileTransfer transfer) {
this(null, transfer);
}
public FileRunnableService(Socket socket, MrimFileTransfer transfer) {
this.socket = socket;
this.transfer = transfer;
if (transfer.files != null) {
currentFileSize = 0;
for (File f : transfer.files) {
currentFileSize += f.length();
}
}
participantUid = transfer.buddyMrid;
setName("File transfer " + transfer.buddyMrid + " " + transfer.messageId);
}
@Override
public void run() {
if (socket == null) {
return;
}
getDataFromSocket();
}
private ExtendedBufferedOutputStream createFile(String filename, long filesize, long modTime, MrimFileTransfer transfer, String participantUid) {
// Dummy
String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
try {
File file = (File) service.getServiceResponse().respond(MrimServiceResponse.RES_GET_FILE_FOR_SAVING, filename, participantUid, modTime);
FileOutputStream fos = new FileOutputStream(file, true);
ExtendedBufferedOutputStream os = new ExtendedBufferedOutputStream(file, fos);
return os;
} catch (IOException e) {
e.printStackTrace();
}
} else {
transferFailed(transfer, "No storage mounted");
}
return null;
}
private void sendFileRequest(MrimIncomingFile file) {
currentFileStream = createFile(file.filename, file.filesize, new Date().getTime(), transfer, participantUid);
byte[] infoBlob;
String str = MRA_GET_FILE + file.filename;
infoBlob = new byte[str.length() + 1];
System.arraycopy(str.getBytes(), 0, infoBlob, 0, infoBlob.length - 1);
infoBlob[infoBlob.length - 1] = 0;
currentFileSize = file.filesize;
currentFileSizeLeft = file.filesize;
connectionState = CONNSTATE_FILE_BODY;
sendToSocket(infoBlob);
}
private void sendProxyHandshake() {
connectionState = CONNSTATE_CONNECTED;
try {
sendToSocket(getProxyHandshakeData(transfer));
} catch (MrimException e) {
service.log(e);
transferFailed(transfer, e.getLocalizedMessage());
notifyFail(transfer);
cleanup();
}
}
private void sendHandshake() {
connectionState = CONNSTATE_HANDSHAKE;
sendToSocket(getHandshakeData(transfer));
}
public byte[] getProxyHandshakeData(MrimFileTransfer transfer) throws MrimException {
MrimPacket packet = new MrimPacket();
packet.type = MrimConstants.MRIM_CS_PROXY_HELLO;
packet.rawData = transfer.proxySessionId;
return service.processor.packet2Bytes(packet);
}
private void getDataFromSocket() {
int read = 0;
boolean fullPacket = true;
final List<byte[]> tmpBlobs = new LinkedList<byte[]>();
while (connectionState != CONNSTATE_DISCONNECTED && socket != null && socket.isConnected() && !socket.isClosed()) {
InputStream is;
try {
is = socket.getInputStream();
if (is.available() < 1) {
Thread.sleep(300);
} else {
Thread.sleep(500);
switch (connectionState) {
case CONNSTATE_CONNECTED:
byte[] head = new byte[44];
is.read(head, 0, 44);
service.log("-- FT got " + ProtocolUtils.getSpacedHexString(head));
int ack = ProtocolUtils.bytes2IntLE(head, 12);
if (ack == MrimConstants.MRIM_CS_PROXY_HELLO_ACK) {
connectionState = CONNSTATE_HANDSHAKE;
if (transfer.files == null) {
sendHandshake();
}
}
break;
case CONNSTATE_HANDSHAKE:
case CONNSTATE_FILE_HEADER:
read = 0;
byte[] blob = new byte[is.available()];
is.read(blob, 0, blob.length);
String str = new String(blob);
service.log("-- FT got " + ProtocolUtils.getSpacedHexString(blob) + " (" + str + ")");
connectionState = CONNSTATE_FILE_HEADER;
if (str.contains(MRA_FT_HELLO)) {
if (transfer.files != null) { // i am sender
if (transfer.connection != MrimFileTransfer.CONN_MIRROR) {
sendHandshake();
}
} else {
if (transfer.connection == MrimFileTransfer.CONN_MIRROR) {
sendHandshake();
}
// service.getRunnableService().sendToSocket(getAcceptMessage(transfer));
sendFileRequest(transfer.incomingFiles.remove(0));
}
break;
}
if (str.contains(MRA_GET_FILE)) {
processHeader(blob);
}
break;
case CONNSTATE_FILE_BODY:
if (buffer == null) {
buffer = new byte[88000];
}
read = is.read(buffer, 0, buffer.length);
service.log("read " + read + "| bytes left " + currentFileSizeLeft);
currentFileSizeLeft -= read;
fileData(buffer, read, currentFileSizeLeft);
if (currentFileSizeLeft < 1) {
// sendFileAck();
connectionState = CONNSTATE_FILE_HEADER;
buffer = null;
if (transfer.incomingFiles != null) {
if (transfer.incomingFiles.size() > 0) {
sendFileRequest(transfer.incomingFiles.remove(0));
} else {
cleanup();
}
}
if (transfer.files != null && transfer.files.size() < 1) {
cleanup();
}
}
continue;
}
if (fullPacket) {
byte[] boo = new byte[0];
for (byte[] i : tmpBlobs) {
boo = ProtocolUtils.concatByteArrays(boo, i);
}
tmpBlobs.clear();
blobs.add(boo);
}
}
} catch (IOException e) {
service.log(e);
} catch (InterruptedException e) {
service.log(e);
}
}
cleanup();
}
private synchronized void fileData(byte[] blob, int read, final long bytesLeft) {
if (currentFileStream != null) {
try {
/*
* if (connectionState == CONNSTATE_FILE_HEADER){
* currentFileStream.close(); currentFileStream = null; }
* else if (connectionState == CONNSTATE_FILE_BODY){
* currentFileStream.write(blob); }
*/
if (connectionState == CONNSTATE_FILE_BODY) {
currentFileStream.write(blob, 0, read);
currentFileStream.flush();
if (bytesLeft < 1) {
connectionState = CONNSTATE_FILE_HEADER;
final String filename = currentFileStream.file.getAbsolutePath();
currentFileStream.close();
currentFileStream = null;
service.log(filename + " got");
sendNotification(transfer.messageId, filename, currentFileSize, currentFileSize - bytesLeft, true, null, participantUid);
} else {
sendNotification(transfer.messageId, currentFileStream.getFile().getAbsolutePath(), currentFileSize, currentFileSize - bytesLeft, true, null, participantUid);
}
}
// messageId, filename, totalSize, sizeTransferred,
// isReceive, error
} catch (IOException e) {
service.log(e);
try {
currentFileStream.close();
} catch (IOException e1) {
service.log(e);
}
currentFileStream = null;
}
}
}
private void cleanup() {
try {
transfers.remove(transfer);
activeTransfers.remove(transfer.messageId);
if (server != null) {
server.close();
server = null;
}
socket.close();
} catch (IOException e) {
service.log(e);
}
}
public synchronized boolean sendToSocket(byte[] out) {
try {
OutputStream os = socket.getOutputStream();
service.log("-- FT To be sent " + ProtocolUtils.getSpacedHexString(out) + "/" + new String(out));
os.write(out);
} catch (IOException e) {
connectionState = CONNSTATE_DISCONNECTED;
service.log(e);
}
return true;
}
private void processHeader(byte[] blob) {
if (blob.length < 1) {
return;
}
String str = ProtocolUtils.getEncodedString(blob, 0, blob.length - 1);
int index = str.indexOf(MRA_GET_FILE);
if (index < 0) {
return;
}
service.log("got header");
currentFileInfo = blob;
try {
String fileInfo = str.substring(index + MRA_GET_FILE.length());
service.log(transfer.buddyMrid + " asks for file " + fileInfo);
for (int i = 0; i < transfer.files.size(); i++) {
File fi = transfer.files.get(i);
if (fi.getName().replaceAll(IN_DATA_DIVIDER, "_").replaceAll(LIST_DATA_DIVIDER, "_").equals(fileInfo)) {
sendFileToSocket(fi);
transfer.files.remove(fi);
if (transfer.files.size() < 1) {
cleanup();
}
return;
}
}
transferFailed(transfer, "unknown file");
} catch (Exception e) {
service.log(e);
transferFailed(transfer, e.getLocalizedMessage());
}
cleanup();
notifyFail(transfer);
}
private void sendFileToSocket(final File file) {
OutputStream os;
try {
os = socket.getOutputStream();
} catch (IOException e) {
service.log(e);
transferFailed(transfer, e.getLocalizedMessage());
notifyFail(transfer);
cleanup();
return;
}
long length = file.length();
if (length > 8000) {
buffer = new byte[8000];
} else {
buffer = new byte[(int) length];
}
currentFileSizeLeft = 0;
int read = 0;
service.log("sending " + file.getName() + " to " + participantUid);
FileInputStream fis;
try {
fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis, 8000);
while (currentFileSizeLeft < length) {
read = bis.read(buffer, 0, buffer.length);
if (read < 0) {
break;
}
os.write(buffer, 0, read);
os.flush();
currentFileSizeLeft += read;
service.log("sent " + currentFileSizeLeft + " bytes");
sendNotification(transfer.messageId, file.getAbsolutePath(), length, currentFileSizeLeft, false, null, participantUid);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
service.log(e);
}
}
} catch (IOException e) {
service.log(e);
transferFailed(transfer, e.getLocalizedMessage());
notifyFail(transfer);
cleanup();
return;
}
connectionState = CONNSTATE_FILE_HEADER;
service.log(file.getName() + " sent");
}
private class ExtendedBufferedOutputStream extends BufferedOutputStream {
private final File file;
public ExtendedBufferedOutputStream(File file, OutputStream os) {
super(os, 88000);
this.file = file;
}
public File getFile() {
return file;
}
}
}
public void parseFTRequest(MrimPacket packet) {
MrimFileTransfer transfer = new MrimFileTransfer();
int version = ProtocolUtils.bytes2IntLE(packet.rawData, 4);
if (version > MrimConstants.PROTO_VERSION) {
int i = 44;
transfer.buddyMrid = MrimEntityAdapter.lpsa2String(packet.rawData, i);
i += 4 + transfer.buddyMrid.length();
transfer.messageId = ProtocolUtils.bytes2IntLE(packet.rawData, i);
i += 4;
i += 4; // total filesize - useless
i += 4; // total file info size - useless
String fileStr = MrimEntityAdapter.lpsa2String(packet.rawData, i);
parseFiles(fileStr, transfer);
i += 4 + fileStr.length();
int nextPacket = ProtocolUtils.bytes2IntLE(packet.rawData, i);
i += 4;
if (nextPacket != 0) {
int count = ProtocolUtils.bytes2IntLE(packet.rawData, i);
i += 4;
for (int ii = 0; ii < count; ii++) {
fileStr = MrimEntityAdapter.lpsw2String(packet.rawData, i);
i += 4 + 2 * fileStr.length();
parseFiles(fileStr, transfer);
}
}
String ipData = MrimEntityAdapter.lpsa2String(packet.rawData, i);
parseIPString(transfer, ipData);
transfers.add(transfer);
} else {
int i = 44;
transfer.buddyMrid = MrimEntityAdapter.lpsa2String(packet.rawData, i);
i += 4 + transfer.buddyMrid.length();
transfer.messageId = ProtocolUtils.bytes2IntLE(packet.rawData, i);
i += 4;
i += 4; // total filesize - useless
i += 4; // total file info size - useless
String fileStr = MrimEntityAdapter.lpsa2String(packet.rawData, i);
String[] strFiles = fileStr.split(LIST_DATA_DIVIDER);
transfer.incomingFiles = new LinkedList<MrimIncomingFile>();
for (String file : strFiles) {
if (file.length() < 1) {
continue;
}
String[] attrs = file.split(IN_DATA_DIVIDER);
MrimIncomingFile ifile = new MrimIncomingFile();
ifile.filename = attrs[0];
ifile.filesize = Long.parseLong(attrs[1]);
transfer.incomingFiles.add(ifile);
}
i += 4 + fileStr.length();
i += 4; // data divider
String ipData = MrimEntityAdapter.lpsa2String(packet.rawData, i);
parseIPString(transfer, ipData);
transfers.add(transfer);
}
service.getServiceResponse().respond(MrimServiceResponse.RES_FILEMESSAGE, transfer);
}
private void parseFiles(String fileStr, MrimFileTransfer transfer) {
String[] strFiles = fileStr.split(LIST_DATA_DIVIDER);
transfer.incomingFiles = new LinkedList<MrimIncomingFile>();
for (int ii = 0; ii < strFiles.length;) {
MrimIncomingFile ifile = new MrimIncomingFile();
ifile.filename = strFiles[ii];
ii++;
ifile.filesize = Long.parseLong(strFiles[ii]);
transfer.incomingFiles.add(ifile);
ii++;
}
}
public void parseFTResponse(MrimPacket packet) {
int i = 44;
int status = ProtocolUtils.bytes2IntLE(packet.rawData, i);
i += 4;
String from = MrimEntityAdapter.lpsa2String(packet.rawData, i);
i += from.length() + 4;
int msgId = ProtocolUtils.bytes2IntLE(packet.rawData, i);
i += 4;
service.log("Ft response " + status + " from " + from + " for " + msgId);
MrimFileTransfer transfer = findTransfer(msgId);
if (transfer == null)
return;
switch (status) {
case MrimConstants.FILE_TRANSFER_STATUS_OK:
break;
case MrimConstants.FILE_TRANSFER_STATUS_DECLINE: // here so far
transferFailed(transfer, "Cancelled");
break;
case MrimConstants.FILE_TRANSFER_STATUS_INCOMPATIBLE_VERS:
case MrimConstants.FILE_TRANSFER_STATUS_ERROR:
transferFailed(transfer, "remote error");
break;
case MrimConstants.FILE_TRANSFER_MIRROR:
String ipData = MrimEntityAdapter.lpsa2String(packet.rawData, i);
transfer.connection = MrimFileTransfer.CONN_MIRROR;
parseIPString(transfer, ipData);
connectPeer(transfer, null);
break;
}
}
private void parseIPString(MrimFileTransfer transfer, String ipData) {
String[] ipDataParts = ipData.split(LIST_DATA_DIVIDER);
for (String data : ipDataParts) {
if (data.length() < 1) {
continue;
}
String[] connection = data.split(IN_DATA_DIVIDER);
transfer.host = connection[0];
transfer.port = Integer.parseInt(connection[1]);
if (transfer.port != 443) {// TODO do we need ssl? do we need more
// ips?
break;
}
}
}
private MrimFileTransfer findTransfer(int msgId) {
for (MrimFileTransfer tr : transfers) {
if (tr.messageId == msgId) {
return tr;
}
}
return null;
}
private void connectPeer(MrimFileTransfer transfer, FileRunnableService runnable) {
service.log("connecting peer " + transfer.host + ":" + transfer.port + " for " + transfer.messageId + "//receiver ");
Socket socket;
try {
socket = new Socket();
socket.connect(new InetSocketAddress(InetAddress.getByAddress(ProtocolUtils.ipString2ByteBE(transfer.host)), transfer.port), 10000);
} catch (Exception e) {
service.log(e);
socket = null;
}
if (socket != null && socket.isConnected()) {
service.log("ft: direct socket connected for " + transfer.messageId);
if (runnable == null) {
service.log("ft: new runnable for " + transfer.messageId);
runnable = new FileRunnableService(socket, transfer);
activeTransfers.put((long) transfer.messageId, runnable);
} else {
service.log("ft: existing runnable for " + transfer.messageId);
if (runnable.server != null) {
try {
runnable.server.close();
runnable.server = null;
} catch (IOException e) {
service.log(e);
}
}
if (runnable.socket != null) {
try {
runnable.socket.close();
} catch (IOException e) {
service.log(e);
}
}
runnable.socket = socket;
}
runnable.start();
if (transfer.connection == MrimFileTransfer.CONN_MIRROR) {
runnable.sendHandshake();
}
if (transfer.connection == MrimFileTransfer.CONN_PROXY) {
runnable.sendProxyHandshake();
}
} else {
if (runnable == null) {
runnable = new FileRunnableService(socket, transfer);
}
try {
if (runnable.server != null) {
runnable.server.close();
runnable.server = null;
}
} catch (IOException e) {
service.log(e);
}
runnable.socket = socket;
service.log("ft: no direct connection");
if (transfer.connection == MrimFileTransfer.CONN_PEER && transfer.files == null) {
createMirror(transfer);
} else if (transfer.connection == MrimFileTransfer.CONN_MIRROR && transfer.files != null) {
createProxyCall(transfer);
} else {
transferFailed(transfer, "no route to host");
notifyFail(transfer);
}
}
}
private void createProxyCall(MrimFileTransfer transfer) {
try {
transfer.connection = MrimFileTransfer.CONN_PROXY;
InetAddress inetAdd = InetAddress.getByName(MrimProxyName);
transfer.host = inetAdd.getHostAddress();
transfer.port = 2041;
service.getRunnableService().sendToSocket(getFTProxyRedirectCall(transfer));
} catch (Exception uhe) {
service.log(uhe);
}
}
private MrimPacket getFTProxyRedirectCall(MrimFileTransfer transfer) throws IOException {
MrimPacket packet = new MrimPacket();
packet.type = MrimConstants.MRIM_CS_PROXY;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
stream.write(MrimEntityAdapter.string2lpsa(transfer.buddyMrid));
stream.write(ProtocolUtils.int2ByteLE(transfer.messageId));
stream.write(ProtocolUtils.int2ByteLE(MrimConstants.MRIM_PROXY_TYPE_FILES));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < transfer.files.size(); i++) {
File file = transfer.files.get(i);
sb.append(file.getName().replaceAll(IN_DATA_DIVIDER, "_").replaceAll(LIST_DATA_DIVIDER, "_"));
// sb.append(IN_DATA_DIVIDER);
sb.append(LIST_DATA_DIVIDER);
sb.append(file.length());
sb.append(LIST_DATA_DIVIDER);
}
stream.write(MrimEntityAdapter.string2lpsa(sb.toString()));
stream.write(MrimEntityAdapter.string2lpsa(transfer.host + IN_DATA_DIVIDER + transfer.port + LIST_DATA_DIVIDER));
/*
* transfer.proxySessionId = new byte[16];
*
* Random random = new Random();
* System.arraycopy(ProtocolUtils.long2ByteLE(random.nextLong()), 0,
* transfer.proxySessionId, 0, 8);
* System.arraycopy(ProtocolUtils.long2ByteLE(random.nextLong()), 0,
* transfer.proxySessionId, 8, 8);
* stream.write(transfer.proxySessionId);
*/
stream.write(new byte[16]);
packet.rawData = stream.toByteArray();
return packet;
}
private void createMirror(MrimFileTransfer transfer) {
try {
MrimPacket packet = getAnswerMessage(transfer, MrimConstants.FILE_TRANSFER_MIRROR);
FileRunnableService frs = createPeer(transfer);
String ipData = ProtocolUtils.getIPString(localIp) + IN_DATA_DIVIDER + frs.server.getLocalPort();
packet.rawData = ProtocolUtils.concatByteArrays(packet.rawData, MrimEntityAdapter.string2lpsa(ipData));
transfer.connection = MrimFileTransfer.CONN_MIRROR;
service.getRunnableService().sendToSocket(packet);
} catch (IOException e) {
transferFailed(transfer, e.toString());
notifyFail(transfer);
}
}
private void notifyFail(MrimFileTransfer transfer) {
service.getRunnableService().sendToSocket(getAnswerMessage(transfer, MrimConstants.FILE_TRANSFER_STATUS_ERROR));
}
private void transferFailed(MrimFileTransfer transfer, String error) {
service.log(error);
transfers.remove(transfer);
activeTransfers.remove(transfer.messageId);
sendNotification(transfer.messageId, transfer.buddyMrid, 0L, 0L, false, error, transfer.buddyMrid);
}
@SuppressWarnings("unused")
private MrimPacket getAcceptMessage(MrimFileTransfer transfer) {
return getAnswerMessage(transfer, MrimConstants.FILE_TRANSFER_STATUS_OK);
}
private MrimPacket getAnswerMessage(MrimFileTransfer transfer, int ftStatus) {
MrimPacket packet = new MrimPacket();
packet.type = MrimConstants.MRIM_CS_FILE_TRANSFER_ACK;
byte[] mrid = MrimEntityAdapter.string2lpsa(transfer.buddyMrid);
byte[] blob = new byte[8 + mrid.length];
int i = 0;
System.arraycopy(ProtocolUtils.int2ByteLE(ftStatus), 0, blob, i, 4);
i += 4;
System.arraycopy(mrid, 0, blob, i, mrid.length);
i += mrid.length;
System.arraycopy(ProtocolUtils.int2ByteLE(transfer.messageId), 0, blob, i, 4);
packet.rawData = blob;
return packet;
}
private synchronized void sendNotification(int messageId, String filename, long totalSize, long sizeSent, boolean incoming, String error, String participantUid) {
NotificationData data = new NotificationData(messageId, filename, totalSize, sizeSent, incoming, error, participantUid);
notifications.add(data);
new Thread("Notification") {
@Override
public void run() {
sendNotifications();
}
}.start();
}
private void sendNotifications() {
synchronized (notifications) {
while (notifications.size() > 0) {
NotificationData data = notifications.remove(0);
service.getServiceResponse().respond(MrimServiceResponse.RES_FILEPROGRESS, (long) data.messageId, data.filePath, data.totalSize, data.sent, data.incoming, data.error, data.participantUid);
}
}
}
private class NotificationData {
public int messageId;
public String filePath;
public long totalSize;
public long sent;
public boolean incoming;
public String error;
public String participantUid;
public NotificationData(int messageId, String filePath, long totalSize, long sent, boolean incoming, String error, String participantUid) {
this.messageId = messageId;
this.filePath = filePath;
this.totalSize = totalSize;
this.sent = sent;
this.incoming = incoming;
this.error = error;
this.participantUid = participantUid;
}
}
public void cancelAll() {
for (FileRunnableService runnable : activeTransfers.values()) {
if (runnable.server != null && !runnable.server.isClosed()) {
try {
runnable.server.close();
} catch (IOException e) {
service.log(e);
}
}
if (runnable.socket != null && !runnable.socket.isClosed()) {
try {
runnable.socket.close();
} catch (IOException e) {
service.log(e);
}
}
}
}
public void fileReceiveResponse(Long msgId, Boolean accept, byte[] internalIp) {
MrimFileTransfer transfer = findTransfer(msgId.intValue());
if (transfer == null) {
service.log("ft: no message");
return;
}
this.localIp = internalIp;
if (!accept) {
service.log("ft: reject " + transfer.messageId);
service.getRunnableService().sendToSocket(getAnswerMessage(transfer, MrimConstants.FILE_TRANSFER_STATUS_DECLINE));
} else {
service.log("ft: accept " + transfer.messageId);
connectPeer(transfer, null);
}
}
public void parseFTProxyConnectionRequest(MrimPacket packet) {
int pos = 44;
String email = MrimEntityAdapter.lpsa2String(packet.rawData, pos);
service.log("proxy " + email);
pos += email.length() + 4;
int id = ProtocolUtils.bytes2IntLE(packet.rawData, pos);
pos += 4;
service.log("proxy id " + id);
int dataType = ProtocolUtils.bytes2IntLE(packet.rawData, pos);
pos += 4;
service.log("proxy type " + dataType);
if (dataType != MrimConstants.MRIM_PROXY_TYPE_FILES) {
return;
}
MrimFileTransfer transfer = findTransfer(id);
if (transfer == null) {
return;
}
String rawdata = MrimEntityAdapter.lpsa2String(packet.rawData, pos);
service.log("proxy data " + rawdata);
parseFiles(rawdata, transfer);
pos += rawdata.length() + 4;
String iplist = MrimEntityAdapter.lpsa2String(packet.rawData, pos);
service.log("proxy iplist " + iplist);
pos += iplist.length() + 4;
transfer.proxySessionId = new byte[16];
System.arraycopy(packet.rawData, pos, transfer.proxySessionId, 0, 16);
pos += 16;
byte[] answerData = new byte[packet.rawData.length - 40];
System.arraycopy(ProtocolUtils.int2ByteLE(MrimConstants.PROXY_STATUS_OK), 0, answerData, 0, 4);
System.arraycopy(packet.rawData, 44, answerData, 4, packet.rawData.length - 44);
MrimPacket ack = new MrimPacket();
ack.type = MrimConstants.MRIM_CS_PROXY_ACK;
ack.rawData = answerData;
service.getRunnableService().sendToSocket(ack);
transfer.connection = MrimFileTransfer.CONN_PROXY;
parseIPString(transfer, iplist);
connectPeer(transfer, activeTransfers.get(id));
}
public void parseFTProxyAck(MrimPacket packet) {
int pos = 44;
int result = ProtocolUtils.bytes2IntLE(packet.rawData, pos);
pos += 4;
String email = MrimEntityAdapter.lpsa2String(packet.rawData, pos);
service.log("proxy " + email);
pos += email.length() + 4;
int id = ProtocolUtils.bytes2IntLE(packet.rawData, pos);
pos += 4;
service.log("proxy id " + id);
int dataType = ProtocolUtils.bytes2IntLE(packet.rawData, pos);
pos += 4;
service.log("proxy type " + dataType);
if (dataType != MrimConstants.MRIM_PROXY_TYPE_FILES) {
return;
}
MrimFileTransfer transfer = findTransfer(id);
if (transfer == null) {
return;
}
FileRunnableService frs = activeTransfers.get(id);
service.log("proxy id " + id);
if (result != MrimConstants.PROXY_STATUS_OK) {
service.log("Proxy error " + result);
transferFailed(transfer, "Proxy error");
notifyFail(transfer);
if (frs != null) {
frs.cleanup();
}
return;
}
if (transfer.proxySessionId != null) {
service.log("proceed file send " + frs.connectionState);
return;
}
String rawdata = MrimEntityAdapter.lpsa2String(packet.rawData, pos);
service.log("proxy data " + rawdata);
parseFiles(rawdata, transfer);
pos += rawdata.length() + 4;
String iplist = MrimEntityAdapter.lpsa2String(packet.rawData, pos);
service.log("proxy iplist " + iplist);
pos += iplist.length() + 4;
transfer.proxySessionId = new byte[16];
System.arraycopy(packet.rawData, pos, transfer.proxySessionId, 0, 16);
pos += 16;
parseIPString(transfer, iplist);
connectPeer(transfer, frs);
}
}